package com.nuoniu.sibanyun.filter;

import com.alibaba.fastjson.JSONObject;
import com.nuoniu.sibanyun.authException.TokenErrorException;
import com.nuoniu.sibanyun.common.result.HttpResult;
import com.nuoniu.sibanyun.common.result.ResultStatusCode;
import com.nuoniu.sibanyun.common.web.http.UserAgentUtils;
import com.nuoniu.sibanyun.config.JwtUtils;
import com.nuoniu.sibanyun.config.SpringContextHolder;
import com.nuoniu.sibanyun.service.msg.WebSocketService;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;

/**
 * 验证成功当然就是进行鉴权了
 * 登录成功之后走此类进行鉴权操作
 */
@Log4j2
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    private  JwtUtils jwtUtils =  SpringContextHolder.getBean(JwtUtils.class);

    private WebSocketService socketService = SpringContextHolder.getBean(WebSocketService.class);

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {

        String tokenHeader;
        tokenHeader = request.getHeader(JwtUtils.TOKEN_HEADER);
        if(StringUtils.isBlank(tokenHeader)) {
            tokenHeader = request.getHeader("Sec-WebSocket-Protocol");
            if(StringUtils.isNotBlank(tokenHeader)) {
                tokenHeader = "Bearer " + tokenHeader;
            }
        }
        // 如果请求头中没有Authorization信息则直接放行了
        if (tokenHeader == null || !tokenHeader.startsWith(JwtUtils.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        // 如果请求头中有token，则进行解析，并且设置认证信息
        SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader,request,response));

        //跳转到 UserInterceptor 拦截器
        super.doFilterInternal(request, response, chain);
        UserThreadLocal.remove();
    }

    // 这里从token中获取用户信息并新建一个token
    // *** 验证token 的有效性  ***
    private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader,HttpServletRequest request,
                                                                  HttpServletResponse response) {
        String token = tokenHeader.replace(JwtUtils.TOKEN_PREFIX, "");
        String wsHeader = request.getHeader("Sec-WebSocket-Protocol");
        String header = request.getHeader(JwtUtils.TOKEN_HEADER);
        //判断 请求是否来自移动端
        boolean isMobile = UserAgentUtils.checkAgentIsMobile(request.getHeader("User-Agent"));
        try {
                String userName = jwtUtils.getUserFromToken(token);
                //从redis中 获取用户
                UserInfo redisUserInfo = jwtUtils.getRedisUserInfo(userName);
                redisUserInfo.setIsMobile(isMobile);
                UserThreadLocal.set(redisUserInfo);
                //每次认证把uId塞入UserIdThreadLocal
                //这样可以在当前请求线程里面 通过 UidHelper.get()获取用户id
//                UidHelper.set(userId.split("-")[0]);
                //webSocket连接 不做token更新逻辑
                if(StringUtils.isNotBlank(wsHeader) ) {
                    log.debug("WS连接，不做权限校验，直接返回传入的token：{}",wsHeader);
                    response.setHeader("Sec-WebSocket-Protocol", wsHeader);
                }
                if(StringUtils.isNotBlank(header)){
                    //判断token的有效性，并判断token是否需要更新,返回当前可用token
                    String newToken = jwtUtils.getGoodToken(token,isMobile);
                    if(!token.equals(newToken)){
                        response.setHeader("Access-Control-Expose-Headers", JwtUtils.TOKEN_HEADER);
                        String tokenStr = JwtUtils.TOKEN_PREFIX + newToken;
                        response.setHeader(JwtUtils.TOKEN_HEADER, tokenStr);
                    }
                }
                //从redis中获取权限(todo )
                Collection<GrantedAuthority> userAuthorityFromRedis = jwtUtils.getUserAuthorityFromRedis(userName);
                return new UsernamePasswordAuthenticationToken(userName, null,
                        userAuthorityFromRedis);
        } catch (Exception e) {
//            e.printStackTrace();
            log.error("Token已过期,token:{},Message:{}",token,e.getMessage());
        }
        return null;
    }

    @Override
    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        HttpResult httpResult;
        if (failed instanceof TokenErrorException) {
            httpResult = HttpResult.error(ResultStatusCode.INVALID_TOKEN.getCode(),failed.getMessage());
        }  else {
            httpResult = HttpResult.error(ResultStatusCode.USER_UNAUTHO_FIAL);
        }
        response.setStatus(401);
        response.getWriter().print(JSONObject.toJSONString(httpResult));
    }
}
